package com.health.trans.kit;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.health.trans.pojo.CtTimeDto;
import com.health.trans.pojo.FieldInfo;
import com.health.trans.pojo.TableInfo;
import com.health.trans.pojo.TransParam;
import com.health.trans.util.DataSourceUtil;
import lombok.Getter;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.EntityManager;
import javax.persistence.criteria.*;
import java.io.Serializable;
import java.util.*;

/**
 * @author nqlz
 */
public class BaseSpecific<T> implements Serializable, Cloneable {
    private final TransParam param;
    private final List<FieldInfo> infoQuery;
    private final TableInfo<T> tableInfo;
    private final String optimizedClassName;
    @Getter
    private final List<Predicate> predicateList = new ArrayList<>();

    public Specification<T> init() {
        String hospCode = param.getReader().getHospCode();
        return (Specification<T>) (root, query, cb) -> {
            //hospCode
            if (StrUtil.isNotBlank(hospCode)) {
                predicateList.add(cb.equal(root.get("hospCode").as(String.class), hospCode));
            }

            //日期
            param.getReader().getDatePredicate((stTime, edTime) -> predicateList.add(cb.between(root.get("createdTime").as(Date.class), stTime, edTime)));

            //其他条件
            if (CollUtil.isNotEmpty(infoQuery)) {
                infoQuery.stream()
                        .filter(i -> !StrUtil.isNotBlank(hospCode) || !"hospCode".equals(i.getName()))
                        .forEach(i -> SpecificKit.fieldInfoCondition(i, predicateList, root, cb));
            }
            Predicate[] pre = new Predicate[predicateList.size()];

            pre = predicateList.toArray(pre);

            return query.where(pre).getRestriction();
        };
    }

    public BaseSpecific(TransParam param, TableInfo<T> tableInfo, String optimizedClassName) {
        this.param = param;
        this.infoQuery = tableInfo.getFieldInfos();
        this.optimizedClassName = optimizedClassName;
        this.tableInfo = tableInfo;
    }


    public Specification<T> specifByCt(CtTimeDto ctTimeDto) {
        return (Specification<T>) (root, query, cb) -> {
            List<Predicate> predicates = CollUtil.newArrayList(predicateList);
            predicates.add(cb.greaterThanOrEqualTo(root.get(optimizedClassName).as(Date.class), ctTimeDto.getMinDate()));
            predicates.add(cb.lessThan(root.get(optimizedClassName).as(Date.class), ctTimeDto.getTargetDate()));
            Predicate[] pre = new Predicate[predicates.size()];
            pre = predicates.toArray(pre);
            return query.where(pre).getRestriction();
        };
    }

    public CtTimeDto getFirstCtTime(Class<T> tClass) {
        EntityManager em = DataSourceUtil.getEntityManager(param.getReader().getSource());
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Object> cq = cb.createQuery();
        Root<T> root = cq.from(tClass);
        List<Predicate> predicates = CollUtil.newArrayList(predicateList);
        Predicate[] pre = new Predicate[predicates.size()];
        Path path = (Path) root.get(optimizedClassName).as(Date.class);
        cq.multiselect(cb.greatest(path).alias("maxDate"), cb.least(path).alias("minDate"));
        pre = predicates.toArray(pre);
        Object[] resultList = (Object[]) em.createQuery(cq.where(pre)).getSingleResult();
        return new CtTimeDto().setMinDate((Date) resultList[1]).setMaxDate((Date) resultList[0]);
    }

}
